package com.rao.gmall.seckill.mq;

import com.rabbitmq.client.Channel;
import com.rao.gmall.common.constant.RedisConst;
import com.rao.gmall.model.activity.SeckillGoods;
import com.rao.gmall.mq.MqConst;
import com.rao.gmall.seckill.component.SeckillLocalCache;
import com.rao.gmall.seckill.service.SeckillService;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author nzmxfan
 * @create 2021-08-26-20:46
 */
@Component
public class SeckillMqListener {
    
    @Autowired
    SeckillService seckillService;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    RedissonClient redissonClient;
    /**
     *     public static final String SECKILL_EXCHANGE = "exchange.seckill";
     *     public static final String SECKILL_UPGOOD_RK = "seckill.upgoods";
     *     public static final String SECKILL_UPGOOD_QUEUE = "seckill.upgoods.queue";
     */
    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue(value = MqConst.SECKILL_UPGOOD_QUEUE,durable = "true",autoDelete = "false",exclusive = "false"),
                    exchange = @Exchange(value = MqConst.SECKILL_EXCHANGE,durable = "true",autoDelete = "false",type="topic"),
                    key = {MqConst.SECKILL_UPGOOD_RK}
            )
    })
    public void listenUpGoodsNews(Message message, Channel channel, String date) throws IOException {
        try{
            //this.getCurrentSeckillGoods(); 不去切面，而是去方法体
            //seckilService.getCurrentSeckillGoods();走切面的逻辑
            //redis中会保存商品信息，锁
            //1、查询当前系统需要上架的所有商品并保存到redis
            List<SeckillGoods> goods = seckillService.getCurrentSeckillGoods();
            //让所有的seckill同步商品
            //发给redis一个消息，所有的商品上架了
            //2、发送通知给其他人，同步本地缓存
            redisTemplate.convertAndSend(SeckillLocalCache.channel_key,SeckillLocalCache.goods_up);

            //3、每个商品的库存作为分布式信号量【预热】，非幂等的,
            // 由于定时任务上架商品会给消息队列发很多消息？
            for (SeckillGoods good : goods) {
                //seckill:stock:51 = 8
                RSemaphore semaphore = redissonClient.getSemaphore(RedisConst.SECKILL_STOCK_PREFIX + good.getSkuId());
                int count = good.getNum() - good.getStockCount();
                //准备商品可以秒杀的数量
                //判断如果信号量已经做了就不用做了
                //返回信号量中有多少信号
                //   这样某一刻有人消费量信号量，那不还是会重复累加？不存在的  9:00
//                int result = semaphore.availablePermits();

                //就是redis的信号量是空的 和库存有不一样代表信号量和库存不一致  商品没有售完 如果执行队列就会减 。一样代表商品售完了就不减了是么
                //初始化信号  1:00 就已经做完了
                //五个会同时进入if里面吗？ 确实会有。
                semaphore.trySetPermits(count);
                //设置信号量自动回收机制。当天结束就完成
                semaphore.expire(1, TimeUnit.DAYS);


            }
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        }catch (Exception e){
            channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,true);
        }


    }
}